package com.ruyuan.rapid.core.netty.processor.filter.pre;

import com.ruyuan.rapid.common.config.DynamicConfigManager;
import com.ruyuan.rapid.common.config.ServiceInstance;
import com.ruyuan.rapid.common.constants.ProcessorFilterConstants;
import com.ruyuan.rapid.common.constants.RapidProtocol;
import com.ruyuan.rapid.common.enums.LoadBalanceStrategy;
import com.ruyuan.rapid.common.enums.ResponseCode;
import com.ruyuan.rapid.common.exception.RapidResponseException;
import com.ruyuan.rapid.core.balance.LoadBalance;
import com.ruyuan.rapid.core.balance.LoadBalanceFactory;
import com.ruyuan.rapid.core.context.AttributeKey;
import com.ruyuan.rapid.core.context.Context;
import com.ruyuan.rapid.core.context.RapidContext;
import com.ruyuan.rapid.core.context.RapidRequest;
import com.ruyuan.rapid.core.netty.processor.filter.AbstractEntryProcessorFilter;
import com.ruyuan.rapid.core.netty.processor.filter.Filter;
import com.ruyuan.rapid.core.netty.processor.filter.FilterConfig;
import com.ruyuan.rapid.core.netty.processor.filter.ProcessorFilterType;
import lombok.Getter;
import lombok.Setter;

import java.util.Set;

/**
 * 负载均衡前置过滤器
 * @author mac
 */
@Filter(
        id = ProcessorFilterConstants.LOADBALANCE_PRE_FILTER_ID,
        name = ProcessorFilterConstants.LOADBALANCE_PRE_FILTER_NAME,
        value = ProcessorFilterType.PRE,
        order = ProcessorFilterConstants.LOADBALANCE_PRE_FILTER_ORDER
)
public class LoadBalancePreFilter extends AbstractEntryProcessorFilter<LoadBalancePreFilter.Config> {
    public LoadBalancePreFilter() {
        super(LoadBalancePreFilter.Config.class);
    }

    /**
     * 真正执行过滤器的方法
     * @param ctx
     * @param args
     * @throws Throwable
     */
    @Override
    public void entry(Context ctx, Object... args) throws Throwable {
        try {
            RapidContext rapidContext = (RapidContext) ctx;
            LoadBalancePreFilter.Config config = (Config) args[0];
            LoadBalanceStrategy loadBalanceStrategy = config.getLoadBalanceStrategy();
            String protocol = ctx.getProtocol();
            switch (protocol) {
                case RapidProtocol.HTTP:
                    doHttpLoadBalance(rapidContext, loadBalanceStrategy);
                    break;
                case RapidProtocol.DUBBO:
                    doDubboLoadBalance(rapidContext, loadBalanceStrategy);
                    break;
                default:
                    break;
            }
        } finally {
            super.fireNext(ctx, args);
        }
    }

    private void doDubboLoadBalance(RapidContext rapidContext, LoadBalanceStrategy loadBalanceStrategy) {
        // 将负载均衡策略设置到上下文中即可, 由dubbo自己的loadBalance去进行使用
        rapidContext.putAttribute(AttributeKey.DUBBO_LOADBALANCE_STRATEGY, loadBalanceStrategy);
    }

    private void doHttpLoadBalance(RapidContext rapidContext, LoadBalanceStrategy loadBalanceStrategy) {
        RapidRequest rapidRequest = rapidContext.getRequest();
        String uniqueId = rapidRequest.getUniqueId();
        Set<ServiceInstance> serviceInstances = DynamicConfigManager.getInstance().getServiceInstancesByUniqueId(uniqueId);
        rapidContext.putAttribute(AttributeKey.MATCH_INSTANCES, serviceInstances);

        // 通过负载均衡枚举值,获取负载均衡实例对象
        LoadBalance loadBalance = LoadBalanceFactory.getLoadBalance(loadBalanceStrategy);
        // 选择出一个服务实例
        ServiceInstance serviceInstance = loadBalance.select(rapidContext);
        if (serviceInstance == null) {
            // 如果服务实例没有找到, 终止请求继续执行, 显示抛出异常
            rapidContext.terminated();
            throw new RapidResponseException(ResponseCode.SERVICE_INSTANCE_NOT_FOUND);
        }

        // 这一步非常关键
        rapidContext.getRequestMutable().setModifyHost(serviceInstance.getAddress());
    }

    /**
     * 负载均衡前置过滤器的配置
     */
    @Getter
    @Setter
    public static class Config extends FilterConfig {

        private LoadBalanceStrategy loadBalanceStrategy = LoadBalanceStrategy.RANDOM;
    }
}
